package com.jintian.smart.kernel.flowable.engine;

import com.jintian.smart.kernel.core.annotation.flow.FlowOperate;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IProcessStarter;
import com.jintian.smart.kernel.core.flow.ITaskOperation;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import org.flowable.bpmn.model.ValuedDataObject;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.TaskInfo;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public abstract class AbstractProcessStarter extends BaseProcessService implements IProcessStarter<List<TaskInfo>> {

    @Override
    @Transactional
    @FlowOperate(-1)
    public List<TaskInfo> start(String process, String businessKey, ProcessControl processControl) throws Exception {
        if (processControl == null) {
            processControl = new ProcessControl();
            processControl.setProcess(process);
            processControl.setBusinessKey(businessKey);
        }
        return this.innerExecute(process, businessKey, processControl);
    }

    protected abstract List<TaskInfo> innerExecute(String process, String businessKey, ProcessControl processControl)
            throws Exception;

    protected void beforeStart(String processKey, Integer processVersion, DefaultProcessContext processContext) throws Exception {
        ProcessControl processControl = processContext.getProcessControl();
        // 1.构建runtime
        Map<String, Object> context = new HashMap<>();
        context.putAll(processControl.getBizVariables());
        context.putAll(processControl.getTransientVariables());
        context.putAll(processControl.getVariables());
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_PROCESS)) {
            context.put(ITaskOperation.FORMURL_PARAM_PROCESS, processContext.getProcess());
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_ACTIVITY)) {
            context.put(ITaskOperation.FORMURL_PARAM_ACTIVITY, processContext.getActivity());
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY)) {
            context.put(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY, processContext.getBusinessKey());
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_TASKID)) {
            context.put(ITaskOperation.FORMURL_PARAM_TASKID, null);
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID)) {
            context.put(ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID, null);
        }
        context.put(FlowAction.TRANSIENT_VARNAME_OPERATOR, FlowAction.getFlowOwner());

        DefaultFlowRuntime flowRuntime = this.createFlowRuntime(processKey, processVersion, context);
        processContext.setFlowRuntime(flowRuntime);
        // 2. 应用变量到上下文环境中，开始计算下一环节。(不建议启动规则中使用变量，直接使用表、字段容易理解)
        // 同时传递给流程引擎，例如流程图的skip表达式
        Map<String, Object> processVariables = flowRuntime.applyProcessVariablesToContext();
        for (Entry<String, Object> e : processVariables.entrySet()) {
            if (!processContext.getTransientVariables().containsKey(e.getKey())) {
                processContext.getTransientVariables().put(e.getKey(), e.getValue());
            }
        }
        // 3. 设置流程引擎非持久变量
        if (flowRuntime.getBizBean() != null) {
            processContext.getTransientVariables().put(FlowAction.TRANSIENT_VARNAME_BIZBEAN, flowRuntime.getBizBean());
        }
        processContext.getTransientVariables().put(FlowAction.TRANSIENT_VARNAME_OPERATOR, processContext.getOperator());
    }

    protected DefaultFlowRuntime createFlowRuntime(String processKey, Integer processVersion, Map<String, Object> context) throws Exception {
        ProcessDefinition pd = this.processDefinitionQuery().processDefinitionKey(processKey).latestVersion().singleResult();
        if (pd == null) {
            throw new IllegalArgumentException("流程" + processKey + "尚未发布");
        }
        org.flowable.bpmn.model.Process process = this.getMainProcess(pd.getId());
        List<ValuedDataObject> dataObjects = process.getDataObjects();
        String bizClass = null;
        String modelName = null;
        for (ValuedDataObject obj : dataObjects) {
            if (obj.getId().equals(FlowAction.VARNAME_START_BIZCLASS)) {
                bizClass = (String) obj.getValue();
            } else if (obj.getId().equals(FlowAction.VARNAME_START_MODELNAME)) {
                modelName = (String) obj.getValue();
            }
        }
        DefaultFlowRuntime runtime = this.createFlowRuntime(bizClass, modelName, processKey, processVersion, context);
        return runtime;
    }
}
